/**
 * 
 */
package br.com.ac.fatureweb.view;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.faces.event.ActionEvent;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;

import br.com.ac.fatureweb.enums.EnumStatusCarga;
import br.com.ac.fatureweb.enums.Status;
import br.com.ac.fatureweb.model.Carga;
import br.com.ac.fatureweb.model.Equipamento;
import br.com.ac.fatureweb.model.ItemRoteiro;
import br.com.ac.fatureweb.model.Roteiro;
import br.com.ac.fatureweb.service.NgcRoteiroBean;
import br.com.ac.fatureweb.utilitary.CrudDefaultBean;
import br.com.ac.fatureweb.utilitary.EnumStatusManaged;
import br.com.ac.fatureweb.utilitary.FacesUtils;
import br.com.ac.fatureweb.utilitary.Order;

/**
 * @author aldivone
 *
 */
@Named
@ViewScoped
public class RoteiroBean extends CrudDefaultBean<Roteiro, NgcRoteiroBean> {

	/**
	 * 
	 */
	private static final long serialVersionUID = -7421372403974550502L;

	private Carga carga;

	/*
	 * Propriedades para a função multi-selection das tabelas.
	 */
	private ItemRoteiro[] selectedItens;

	private ItemRoteiro[] selectedOrfao;

	/*
	 * Lista de itens transientes que serão incluídos ou excluídos ao final da
	 * edição do roteiro.
	 */
	private Set<ItemRoteiro> itensToExclude;

	private Set<ItemRoteiro> itensToInclude;

	@Inject
	private NgcRoteiroBean ngcRoteiroBean;

	public RoteiroBean() {
		super();
		addOrder("numero", Order.ASC);
	}

	/**
	 * 
	 */
	@PostConstruct
	public void inicialize() {
		setService(ngcRoteiroBean);
	}

	public List<Carga> getLstCarga() {
		return ngcRoteiroBean.getLstCarga(EnumStatusCarga.DISPONIVEL);
	}

	public List<Status> getLstStatus() {
		return Arrays.asList(Status.values());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * br.com.ac.fatureweb.utilitary.CrudDefaultBean#persist(javax.faces.event
	 * .ActionEvent)
	 */
	@Override
	public void persist(ActionEvent event) {
		gerarRoteiro(event);
	}

	public void gerarRoteiro(ActionEvent event) {
		if (carga == null) {
			FacesUtils
					.addMessageError("A carga para divisão não foi informada."
							+ " Favor selecionar uma na tabela.");
			return;
		}

		ngcRoteiroBean.gerarRoteiro(getCarga().getId(), getEntity()
				.getQtdLeiturista(), getEntity().getDataFaturamento());

		setCurrentState(EnumStatusManaged.CONSULTAR);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * br.com.ac.fatureweb.utilitary.CrudDefaultBean#runBeforeCallingService()
	 */
	@Override
	protected void runBeforeCallingService() {
		if (EnumStatusManaged.EXCLUIR.equals(getCurrentState())) {
			ngcRoteiroBean.desassociarItensRoteiro(new HashSet<ItemRoteiro>(
					getEntity().getLstItemRoteiro()));
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * br.com.ac.fatureweb.utilitary.CrudDefaultBean#runAfterCallingService()
	 */
	@Override
	protected void runAfterCallingService() {

		if (EnumStatusManaged.VISUALIZAR.equals(getCurrentState())) {
			Roteiro roteiro = ngcRoteiroBean.getRoteiro(getEntity().getId());

			setEntity(roteiro);
		} else if (EnumStatusManaged.ALTERAR.equals(getCurrentState())) {
			boolean reordenar = false;

			if (itensToExclude != null && !itensToExclude.isEmpty()) {
				ngcRoteiroBean.desassociarItensRoteiro(itensToExclude);
				getEntity().setQtdImovel(
						getEntity().getQtdImovel() - itensToExclude.size());
				itensToExclude = null;
				reordenar = true;
			}

			if (itensToInclude != null && !itensToInclude.isEmpty()) {
				ngcRoteiroBean
						.associarItensRoteiro(itensToInclude, getEntity());
				getEntity().setQtdImovel(
						getEntity().getQtdImovel() + itensToInclude.size());
				itensToInclude = null;
				reordenar = true;
			}

			if (reordenar)
				reordenar();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * br.com.ac.fatureweb.utilitary.CrudDefaultBean#prepareMerge(javax.faces
	 * .event.ActionEvent)
	 */
	@Override
	public void prepareMerge(ActionEvent event) {
		super.prepareMerge(event);

		if (getEntity().getStatus() != null
				&& !Status.DISPONIVEL.equals(getEntity().getStatus())) {

			setCurrentState(EnumStatusManaged.CONSULTAR);
			FacesUtils
					.addMessageError("Só é possível alterar roteiros que estejam com o(s) status: DISPONÍVEL.");
			return;
		}

		/*
		 * Exibir informações de detalhe.
		 */
		setEntity(ngcRoteiroBean.getRoteiro(getEntity().getId()));

		getEntity()
				.setQtdItensOrfaos(
						ngcRoteiroBean.countItensOrfaos(getEntity().getCarga()
								.getId()));
		getEntity().setQtdItensEmRoteiro(
				ngcRoteiroBean.countItensEmRoteiro(getEntity().getCarga()
						.getId()));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * br.com.ac.fatureweb.utilitary.CrudDefaultBean#prepareRemove(javax.faces
	 * .event.ActionEvent)
	 */
	@Override
	public void prepareRemove(ActionEvent event) {
		super.prepareRemove(event);

		if (getEntity().getStatus() != null
				&& !Status.DISPONIVEL.equals(getEntity().getStatus())) {

			setCurrentState(EnumStatusManaged.CONSULTAR);
			FacesUtils
					.addMessageWarning("Só é possível excluir roteiros que estejam com o(s) status: DISPONÍVEL.");
			return;
		}

		/*
		 * Exibir informações de detalhe.
		 */
		setEntity(ngcRoteiroBean.getRoteiro(getEntity().getId()));
	}

	/**
	 * 
	 * Associar os itens selecionados a um roteiro roteiro.
	 * 
	 * @param event
	 */
	public void associarItensRoteiro(ActionEvent event) {
		if (getSelectedOrfao() != null && getSelectedOrfao().length > 0) {
			if (itensToInclude == null)
				itensToInclude = new HashSet<ItemRoteiro>();

			itensToInclude.addAll(Arrays.asList(getSelectedOrfao()));
			getEntity().getLstItemRoteiro().addAll(itensToInclude);

			/*
			 * Caso um item recém-excluído seja incluído novamente.
			 */
			if (itensToExclude != null) {
				Set<ItemRoteiro> selectResults = new HashSet<ItemRoteiro>();

				for (ItemRoteiro itemToExclude : itensToExclude) {
					if (!itensToInclude.contains(itemToExclude)) {
						selectResults.add(itemToExclude);
					}
				}

				itensToExclude = selectResults;
			}

			/*
			 * Reordenando
			 */
			int ordem = 1;
			for (ItemRoteiro item : getEntity().getLstItemRoteiro()) {
				item.setOrdem(ordem);
				ordem++;
			}

			getEntity()
					.setQtdItensOrfaos(
							getEntity().getQtdItensOrfaos()
									- getSelectedOrfao().length);
			getEntity().setQtdItensEmRoteiro(
					getEntity().getQtdItensEmRoteiro()
							+ getSelectedOrfao().length);

			setSelectedOrfao(null);
		} else {
			FacesUtils.addMessageWarning("Nenhum item selecionado.");
		}
	}

	/**
	 * 
	 * Desassociar os itens selecionados do roteiro.
	 * 
	 * @param event
	 */
	public void desassociarItensRoteiro(ActionEvent event) {
		if (getSelectedItens() != null && getSelectedItens().length > 0) {
			if (itensToExclude == null)
				itensToExclude = new HashSet<ItemRoteiro>();

			itensToExclude.addAll(Arrays.asList(getSelectedItens()));
			getEntity().getLstItemRoteiro().removeAll(itensToExclude);

			/*
			 * Caso um item recém-incluído seja excluído novamente.
			 */
			if (itensToInclude != null) {
				Set<ItemRoteiro> selectResults = new HashSet<ItemRoteiro>();

				for (ItemRoteiro itemToInclude : itensToInclude) {
					if (!itensToExclude.contains(itemToInclude)) {
						selectResults.add(itemToInclude);
					}
				}

				itensToInclude = selectResults;
			}

			/*
			 * Reordenando
			 */
			int ordem = 1;
			for (ItemRoteiro item : getEntity().getLstItemRoteiro()) {
				item.setOrdem(ordem);
				ordem++;
			}

			getEntity()
					.setQtdItensOrfaos(
							getEntity().getQtdItensOrfaos()
									+ getSelectedItens().length);
			getEntity().setQtdItensEmRoteiro(
					getEntity().getQtdItensEmRoteiro()
							- getSelectedItens().length);

			setSelectedItens(null);
		} else {
			FacesUtils.addMessageWarning("Nenhum item selecionado.");
		}
	}

	/**
	 * Procura por itens que ainda não estejam associados a um roteiro.
	 * 
	 * @param event
	 */
	public void buscarItensOrfaos(ActionEvent event) {
		List<ItemRoteiro> lstItemOrfao = ngcRoteiroBean
				.buscarItensOrfaos(getEntity().getCarga().getId());

		/*
		 * Exibir também os itens que foram parcialmente excluídos.
		 */
		if (itensToExclude != null && !itensToExclude.isEmpty()) {
			for (ItemRoteiro itemToExclude : itensToExclude) {
				if (!contains(lstItemOrfao, itemToExclude)) {
					lstItemOrfao.add(itemToExclude);
				}
			}
		}

		/*
		 * Não exibir os itens que já foram parcialmente adicionados.
		 */
		if (itensToInclude != null && !itensToInclude.isEmpty()) {
			List<ItemRoteiro> selectResult = new ArrayList<ItemRoteiro>();
			for (ItemRoteiro itemOrfao : lstItemOrfao) {
				if (contains(itensToInclude, itemOrfao)) {
					selectResult.add(itemOrfao);
				}
			}

			lstItemOrfao.removeAll(selectResult);
		}

		getEntity().setLstItemOrfao(lstItemOrfao);
	}

	/**
	 * Tentei usar o contains da lista, mas sem sucesso. Talvez porque estou
	 * comparando com um item que está dentro de um HashSet.
	 */
	private boolean contains(Set<ItemRoteiro> lista, ItemRoteiro itemRoteiro) {
		for (ItemRoteiro item : lista) {
			if (item.getId().equals(itemRoteiro.getId())) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Tentei usar o contains da lista, mas sem sucesso. Talvez porque estou
	 * comparando com um item que está dentro de um HashSet.
	 */
	private boolean contains(List<ItemRoteiro> lista, ItemRoteiro itemRoteiro) {

		for (ItemRoteiro item : lista) {
			if (item.getId().equals(itemRoteiro.getId())) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Atualizar e reordenar a lista de itens após uma remoção ao adição de um
	 * novo item.
	 */
	private void reordenar() {
		List<ItemRoteiro> lstItemRoteiro = ngcRoteiroBean
				.reordenarItensRoteiro(getEntity().getId());
		getEntity().setLstItemRoteiro(lstItemRoteiro);
	}

	/**
	 * @return the carga
	 */
	public Carga getCarga() {
		return carga;
	}

	/**
	 * @param carga
	 *            the carga to set
	 */
	public void setCarga(Carga carga) {
		this.carga = carga;
	}

	/**
	 * @return the lstEquipamento
	 */
	public List<Equipamento> getLstEquipamento() {
		return ngcRoteiroBean.getLstEquipamento();
	}

	/**
	 * @return the selectedItens
	 */
	public ItemRoteiro[] getSelectedItens() {
		return selectedItens;
	}

	/**
	 * @param selectedItens
	 *            the selectedItens to set
	 */
	public void setSelectedItens(ItemRoteiro[] selectedItens) {
		this.selectedItens = selectedItens;
	}

	/**
	 * @return the selectedOrfao
	 */
	public ItemRoteiro[] getSelectedOrfao() {
		return selectedOrfao;
	}

	/**
	 * @param selectedOrfao
	 *            the selectedOrfao to set
	 */
	public void setSelectedOrfao(ItemRoteiro[] selectedOrfao) {
		this.selectedOrfao = selectedOrfao;
	}
}
